﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Permissions;
using Microsoft.Practices.CompositeUI.Collections;
using Microsoft.Practices.CompositeUI.Utility;

namespace Microsoft.Practices.CompositeUI.Instrumentation
{
    /// <summary>
    /// 	Service that manages the <see cref = "TraceSource" /> objects used in the application.
    /// </summary>
    public class TraceSourceCatalogService : ITraceSourceCatalogService
    {
        #region Fields & Properties

        private readonly Dictionary<string, TraceSource> _sourceDictionary = new Dictionary<string, TraceSource>();

        #endregion

        #region Implementation of ITraceSourceCatalogService

        /// <summary>
        /// 	Raised when a trace source is added to the collection
        /// </summary>
        public event EventHandler<DataEventArgs<TraceSource>> TraceSourceAdded;

        /// <summary>
        /// 	Retrieves a <see cref = "TraceSource" /> by name.
        /// </summary>
        /// <param name = "sourceName">Name of the source to retrieve.</param>
        /// <returns>Always retrieves a valid source. If one 
        /// 	was not previously created, it will be initialized upon retrieval.</returns>
        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        public TraceSource GetTraceSource(string sourceName)
        {
            Guard.ArgumentNotNull(sourceName, "sourceName");
            if (_sourceDictionary.ContainsKey(sourceName))
            {
                return _sourceDictionary[sourceName];
            }
            var source = new TraceSource(sourceName);
            _sourceDictionary.Add(source.Name, source);

            // Only clear the built-in default listener added when the source is initialized with no config.
            if (source.Listeners.Count == 1 && source.Listeners[0] is DefaultTraceListener)
            {
                source.Listeners.Clear();
            }
            source.Listeners.AddRange(Trace.Listeners);

            var traceSourceAdded = TraceSourceAdded;
            if (traceSourceAdded != null)
            {
                traceSourceAdded.Invoke(this, new DataEventArgs<TraceSource>(source));
            }
            return source;
        }

        /// <summary>
        /// 	Returns a <see cref = "ReadOnlyDictionary{TKey,TValue}" /> for all the known trace sources.
        /// </summary>
        /// <returns>The dictionary of trace sources</returns>
        public ReadOnlyDictionary<string, TraceSource> TraceSources
        {
            get
            {
                return new ReadOnlyDictionary<string, TraceSource>(_sourceDictionary);
            }
        }

        #endregion

        #region Add Shared Listener

        /// <summary>
        /// 	Adds the listener to all the trace sources.
        /// </summary>
        public void AddSharedListener(TraceListener listener)
        {
            Guard.ArgumentNotNull(listener, "listener");
            AddSharedListener(listener, string.Empty);
        }

        /// <summary>
        /// 	Adds the listener to all the trace sources with a specific name.
        /// </summary>
        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        public void AddSharedListener(TraceListener listener, string name)
        {
            Guard.ArgumentNotNull(listener, "listener");
            Guard.ArgumentNotNull(name, "name");
            if (!string.IsNullOrEmpty(name))
            {
                listener.Name = name;
            }
            foreach (var pair in _sourceDictionary)
            {
                pair.Value.Listeners.Add(listener);
            }
        }

        #endregion
    }
}